/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.lattice
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	
	
	/**
	 * Extends lattice to add methods for observing asynchronous commands. 
	 */
	public class AsyncLattice extends Lattice
	{
		public function AsyncLattice()
		{
			super();
		}
		
		
		protected var _dispatcher:IEventDispatcher;
		
		/** A dispatcher to observe for success and failure type events. */
		public function watch(dispatcher:IEventDispatcher):AsyncLattice
		{
			_dispatcher = dispatcher;
			return this;
		}
		
		
		protected var _completeTriggers:Array;
		
		/** An array of event types to capture from the dispatcher that signal completion. */
		public function completeTriggers(...rest:Array):AsyncLattice
		{
			_completeTriggers = rest;
			return this;
		}
		
		protected var _completeObj:Object;
		protected var _completeParams:Array;
		
		/** Closure to invoke when dispatcher signals completion. */
		public function onComplete(closure:Function, ...rest:Array):AsyncLattice
		{
			_completeObj = closure;
			_completeParams = rest;
			return this;
		}


		protected var _cancelTriggers:Array;
		
		/** An array of event types to capture from the dispatcher that signal cancelation or failure. */
		public function cancelTriggers(...rest:Array):AsyncLattice
		{
			_cancelTriggers = rest;
			return this;
		}
		
		
		protected var _cancelObj:Object;
		protected var _cancelParams:Array;
		
		/** Closure to invoke if some failure occurs in the observed dispatcher. */
		public function onCancel(closure:Function, ...rest:Array):AsyncLattice
		{
			_cancelObj = closure;
			_cancelParams = rest;
			
			return this;
		}
		
		
		/** Adds listeners to observed dispatcher before its execute closure is invoked */
		override protected function preExecute():void
		{
			if(_dispatcher == null)
			{
				throw new ReferenceError("AsyncLattice must have a dispatcher to watch for events");
			}
			
			for each(var completeTrigger:String in _completeTriggers)
			{
				_dispatcher.addEventListener(completeTrigger, completeHandler);
			}
			
			for each(var cancelTrigger:String in _cancelTriggers)
			{
				_dispatcher.addEventListener(cancelTrigger, cancelHandler);
			}
		}
		
		
		override protected function postExecute():void
		{
			// override so that completeHandler or cancelHandler invoke executeNext().
		}
		
		
		/** Invoked when a complete trigger is handled from observed dispatcher. */
		protected function completeHandler(event:Event):void
		{
			_completeObj && (_completeObj as Function).apply(null, _completeParams);
			
			executeNext();
		}
		
		
		/** Invoked when a cancel trigger is handled from observed dispatcher. */
		protected function cancelHandler(event:Event):void
		{
			_cancelObj && (_cancelObj as Function).apply(null, _cancelParams);
			
			executeNext();
		}
		

		/** Removes listeners from dispatcher before calling executeNext in super class. */
		override public function executeNext():void
		{
			removeListeners();
			
			super.executeNext();
		}
		
		
		/** Public method to remove listeners in case dispatcher never fires complete or fail events. */
		public function removeListeners():void
		{
			for each(var completeTrigger:String in _completeTriggers)
			{
				_dispatcher.removeEventListener(completeTrigger, completeHandler);
			}
			
			for each(var cancelTrigger:String in _cancelTriggers)
			{
				_dispatcher.removeEventListener(cancelTrigger, cancelHandler);
			}
		}
		
	}
}